#include "LecteurPhraseAvecArbre.h"

#include <stdlib.h>
#include <iostream>
using namespace std;

////////////////////////////////////////////////////////////////////////////////

LecteurPhraseAvecArbre::LecteurPhraseAvecArbre(string nomFich) :
ls(nomFich), ts() {
}


////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::analyse() {
    arbre = programme();
    cout << "Syntaxe correcte." << endl;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::programme() {
    // <programme> ::= debut <seq_inst> fin FIN_FICHIER

    sauterSymCour("debut");
    Noeud* si = seqInst();
    sauterSymCour("fin");
    testerSymCour("<FINDEFICHIER>");
    return si;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::seqInst() {
    // <seqInst> ::= <inst> ; { <inst> ; }

    NoeudSeqInst* si = new NoeudSeqInst();
    do {
	si->ajouteInstruction(inst());
	sauterSymCour(";");
    } while ((ls.getSymCour() == "<VARIABLE>") | (ls.getSymCour() == "si") | (ls.getSymCour() == "tantque")
	    | (ls.getSymCour() == "repeter") | (ls.getSymCour() == "pour") | (ls.getSymCour() == "lire")
	    | (ls.getSymCour() == "ecrire") | (ls.getSymCour() == "switch"));
    // tant que le symbole courant est un debut possible d'instruction...
    return si;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::inst() {
    // <inst> ::= <affectation> | <instSi> | <instTq> | <instRepeter> 

    try {
	if (ls.getSymCour() == "si") return instSi();
	else if (ls.getSymCour() == "tantque") return instTq();
	else if (ls.getSymCour() == "repeter") return instRepeter();
	else if (ls.getSymCour() == "pour") return instPour();
	else if (ls.getSymCour() == "switch") return instSwitch();
	else if (ls.getSymCour() == "lire") return lire();
	else if (ls.getSymCour() == "ecrire") return ecrire();
	else return affectation();
    } catch (char const*) {
	throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instSi() {
    // <instSi> ::= si ( <expBool> ) <seqInst> { sinonsi ( <expBool> ) <seqInst> } [ sinon <seqInst> ] finsi

    Noeud* expB = NULL;
    Noeud* seqIn = NULL;
    Noeud* suivant = NULL;
    NoeudInstSi* instS = NULL;

    if (ls.getSymCour() == "si") {
	try {
	    sauterSymCour("si");
	    sauterSymCour("(");
	    expB = expBool();
	    sauterSymCour(")");
	    seqIn = seqInst();
	    suivant = instSi();
	    instS = new NoeudInstSi(expB, seqIn, suivant);
	    sauterSymCour("finsi");
	} catch (char const*) {
	    throw;
	}
    } else if (ls.getSymCour() == "sinonsi") {
	try {
	    sauterSymCour("sinonsi");
	    sauterSymCour("(");
	    expB = expBool();
	    sauterSymCour(")");
	    seqIn = seqInst();
	    suivant = instSi();
	    instS = new NoeudInstSi(expB, seqIn, suivant);
	} catch (char const*) {
	    throw;
	}
    } else if (ls.getSymCour() == "sinon") {
	try {
	    sauterSymCour("sinon");
	    seqIn = seqInst();
	    expB = ts.chercheAjoute(Symbole("1"));
	    instS = new NoeudInstSi(expB, seqIn, suivant);
	} catch (char const*) {
	    throw;
	}
    }

    return instS;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instTq() {
    // <instTq> ::= tantque ( <expBool> ) <seqInst> fintantque

    try {
	sauterSymCour("tantque");
	sauterSymCour("(");
	Noeud* exp = expBool();
	sauterSymCour(")");
	Noeud* seqinst = seqInst();
	sauterSymCour("fintantque");
	return new NoeudInstTantQue(exp, seqinst);
    } catch (char const*) {
	throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instRepeter() {
    // <instRepeter> ::= repeter <seqInst> jusqua ( <expBool> )

    try {
	sauterSymCour("repeter");
	Noeud* seq = seqInst();
	sauterSymCour("jusqua");
	sauterSymCour("(");
	Noeud* exp = expBool();
	sauterSymCour(")");

	return new NoeudInstRepeter(exp, seq);
    } catch (char const*) {
	throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instPour() {
    // <instPour> :: = pour ( <affectation>; <expBool>; <affectation ) <seqInst> finpour  
    Noeud* affect1;
    Noeud* expbool;
    Noeud* affect2;
    Noeud* seqinst;
    try {

	sauterSymCour("pour");
	sauterSymCour("(");
	affect1 = affectation();
	sauterSymCour(";");
	expbool = expBool();
	sauterSymCour(";");
	affect2 = affectation();
	sauterSymCour(")");

	seqinst = seqInst();
	sauterSymCour("finpour");

	return new NoeudInstPour(affect1, expbool, affect2, seqinst);
    } catch (char const*) {
	throw;
    }
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instSwitch() {
    // <instSwitch> :: = switch (<variable>) :  {case <entier> <seqInst> } [ default <seqInst> ] finswitch
   try {
	sauterSymCour("switch");
	sauterSymCour("(");
	Noeud* variable = ts.chercheAjoute(ls.getSymCour());
	ls.suivant();
	sauterSymCour(")");
	NoeudInstSwitch* nswitch = new NoeudInstSwitch(variable);
	do {
	    sauterSymCour("case");
	    Noeud* cas = ts.chercheAjoute(ls.getSymCour());
	    ls.suivant();
	    sauterSymCour(":");
	    nswitch->ajouteCase(cas, seqInst());
	} while (ls.getSymCour() == "case");

	if (ls.getSymCour() == "defaut") {
	    ls.suivant();
	    sauterSymCour(":");
	    nswitch->ajouteCase(variable, seqInst());
	}
	sauterSymCour("finswitch");
	return nswitch;
    } catch (char const*) {
	throw "Switch";
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::affectation() {
    // <affectation> ::= <variable> = <expression>

    try {
	testerSymCour("<VARIABLE>");
	Noeud* var = ts.chercheAjoute(ls.getSymCour());
	ls.suivant();
	sauterSymCour("=");
	Noeud* exp = expression();
	return new NoeudAffectation(var, exp);
    } catch (char const*) {
	throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::lire() {
    // <lire> ::= lire ( <variable> )
    try {
	Noeud* fact = NULL;
	sauterSymCour("lire");
	sauterSymCour("(");
	if (ls.getSymCour() == "<VARIABLE>") {
	    fact = facteur();
	    sauterSymCour(")");
	} else
	    erreur("<variable>");
	return new NoeudInstLire(fact);
    } catch (char const*) {
	throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::ecrire() {
    // <ecrire> ::= ecrire ( <expression> | <chaine> )
    try {
	sauterSymCour("ecrire");
	sauterSymCour("(");
	// pour l'instant les phrases ne sont que des entiers...
	Noeud* phrase = expression();
	sauterSymCour(")");
	return new NoeudInstEcrire(phrase);
    } catch (char const*) {
	throw;
    }
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expression() {
    // <expression> ::= <expressionArit> | (<expBool>) | <CHAINE>

    Noeud* exp;
    try {
	if (ls.getSymCour() == "(") {
	    ls.suivant();
	    exp = expBool();
	    sauterSymCour(")");
	} else if (ls.getSymCour() == "<CHAINE>" || ls.getSymCour() == "<CHAR>") {
	    exp = ts.chercheAjoute(ls.getSymCour());
	    ls.suivant();
        }
	else exp = expArit();
	return exp;
    } catch (char const*) {
	throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expArit() {
    // <expressionArit> ::= <terme> { <opAdd> <terme> }
    try {
	Noeud* term = terme();
	while (ls.getSymCour() == "+" || ls.getSymCour() == "-") {
	    Symbole opA = opAdd();
	    Noeud* termDroit = terme();
	    term = new NoeudOperateurBinaire(opA, term, termDroit);
	}
	return term;
    } catch (char const*) {
	throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::terme() {
    // <terme> ::= <facteur> { <opMult> <facteur> }
    try {
	Noeud* fact = facteur();
	while (ls.getSymCour() == "*" || ls.getSymCour() == "/") {
	    Symbole operateur = opMult();
	    Noeud* factDroit = facteur();
	    fact = new NoeudOperateurBinaire(operateur, fact, factDroit);
	}
	return fact;
    } catch (char const*) {
	throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::facteur() {
    // <facteur> ::= [<opUnaire>] <entier> | [<opUnaire>] <reel> | [<opUnaire>] <variable> 

    Noeud* fact = NULL;
    try {
	if (ls.getSymCour() == "<VARIABLE>" || ls.getSymCour() == "<ENTIER>" || ls.getSymCour() == "<REEL>") {
	    fact = ts.chercheAjoute(ls.getSymCour());
	    ls.suivant();
	} else if (ls.getSymCour() == "-" || ls.getSymCour() == "non") {
	    Symbole s = ls.getSymCour();
	    ls.suivant();
	    fact = new NoeudOperateurUnaire(s, facteur());
	} else erreur("<facteur>");

	return fact;
    } catch (char const*) {
	throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expBool() {
    // <expBool> ::= <relation> { <ou> <relation> }
    Noeud* rel = relation();
    try {
	while (ls.getSymCour() == "ou") {
	    Symbole operateur = ls.getSymCour();
	    Noeud* relDroit = relation();
	    rel = new NoeudOperateurBinaire(operateur, rel, relDroit);
	}
	return rel;
    } catch (char const*) {
	throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::relation() {
    // <relation> ::= <condition> { <et> <condition> }
    try {
	Noeud* cond = condition();
	while (ls.getSymCour() == "et") {
	    Symbole operateur = ls.getSymCour();
	    Noeud* condDroit = relation();
	    cond = new NoeudOperateurBinaire(operateur, cond, condDroit);
	}
	return cond;
    } catch (char const*) {
	throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::condition() {
    // <condition> ::= <expression> { <opRel> <expression> }

    Noeud* exp = expression();
    try {
	while (ls.getSymCour() == "==" || ls.getSymCour() == "!=" || ls.getSymCour() == "<" ||
		ls.getSymCour() == "<=" || ls.getSymCour() == ">" || ls.getSymCour() == ">=") {
	    Symbole operateur = opRel();
	    Noeud* expDroit = expression();
	    exp = new NoeudOperateurBinaire(operateur, exp, expDroit);
	}
	return exp;
    } catch (char const*) {
	throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opAdd() {
    // <opAdd> ::= + | -

    Symbole opAdd;
    try {
	if (ls.getSymCour() == "+" || ls.getSymCour() == "-") {
	    opAdd = ls.getSymCour();
	    ls.suivant();
	} else
	    erreur("<opAdd>");
	return opAdd;
    } catch (char const*) {
	throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opMult() {
    // <opMult> ::= * | /

    Symbole opMult;
    try {
	if (ls.getSymCour() == "*" || ls.getSymCour() == "/") {
	    opMult = ls.getSymCour();
	    ls.suivant();
	} else
	    erreur("<opMult>");
	return opMult;
    } catch (char const*) {
	throw;
    }

}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opRel() {
    // <opRel> ::= == | != | < | <= | > | >=

    Symbole opRel;
    try {
	if (ls.getSymCour() == "==" || ls.getSymCour() == "!=" || ls.getSymCour() == "<" ||
		ls.getSymCour() == "<=" || ls.getSymCour() == ">" || ls.getSymCour() == ">=") {
	    opRel = ls.getSymCour();
	    ls.suivant();
	} else
	    erreur("<opRel>");
	return opRel;
    } catch (char const*) {
	throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opUnaire() {
    // <opUnaire> ::= - | non

    Symbole opUn;
    try {
	if (ls.getSymCour() == "-" || ls.getSymCour() == "non") {
	    opUn = ls.getSymCour();
	    ls.suivant();
	} else
	    erreur("<opUnaire>");
	return opUn;
    } catch (char const*) {
	throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::testerSymCour(string ch) {
    if (ls.getSymCour() != ch) {
	cout << endl << "-------- Erreur ligne " << ls.getLigne()
		<< " - Colonne " << ls.getColonne() << endl << "   Attendu : "
		<< ch << endl << "   Trouve  : " << ls.getSymCour() << endl
		<< endl;
	exit(0); // plus tard, on levera une exception
    }
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::sauterSymCour(string ch) {
    testerSymCour(ch);
    ls.suivant();
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::erreur(string mess) {
    cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne "
	    << ls.getColonne() << endl << "   Attendu : " << mess << endl
	    << "   Trouve  : " << ls.getSymCour() << endl << endl;
    exit(0); // plus tard, on levera une exception
}
